#include "stdafx.h"
#include "CRecorder.h"




CRecorder::CRecorder()
{
	m_pAudioClient = NULL;
	m_pCaptureClient = NULL;

	//手动指定音频格式
	WAVEFORMATEX waveFormat;
	waveFormat.wFormatTag = WAVE_FORMAT_PCM;//使用PCM格式采样
	waveFormat.nChannels = 2;//波形音频数据中的信道数，即声道数
	waveFormat.nSamplesPerSec = 48000;//采样速率，单位Hz
	waveFormat.wBitsPerSample = 16;//量化位数，在WAVE_FORMAT_PCM格式下为8或16
	waveFormat.nBlockAlign =(waveFormat.nChannels*waveFormat.wBitsPerSample) / 8;//(2*16)/8=4
	//平均数据传输速率，单位：byte/s
	waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
	waveFormat.cbSize = 0;//在WAVE_FORMAT_PCM格式中此参数被忽略
	m_pwfx = &waveFormat;
	//m_pwfx = NULL;

	dataStart = 0;

	init();
}

void CRecorder::init()
{
	const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
	const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
	const IID IID_IAudioClient = __uuidof(IAudioClient);
	const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);

	CoInitialize(NULL);//初始化com库

	HRESULT hr;

	//创建多媒体枚举器
	IMMDeviceEnumerator *pEnumerator = NULL;//多媒体枚举器
	hr = CoCreateInstance(
		CLSID_MMDeviceEnumerator, NULL,
		CLSCTX_ALL, IID_IMMDeviceEnumerator,
		(void**)&pEnumerator);

	//获取声卡接口
	IMMDevice *pDevice = NULL;//声卡接口
	hr = pEnumerator->GetDefaultAudioEndpoint(
		eCapture, eConsole, &pDevice);

	//通过声卡接口获取声卡客户端接口
	//IAudioClient *pAudioClient = NULL;//声卡客户端
	m_pAudioClient = NULL;
	hr = pDevice->Activate(
		IID_IAudioClient, CLSCTX_ALL,
		NULL, (void**)&m_pAudioClient);

	//获取音频格式
	//WAVEFORMATEX *pwfx = NULL;
	if (m_pwfx == NULL)
	{//如果未定义格式，则使用默认格式
		m_pAudioClient->GetMixFormat(&m_pwfx);
	}

	//初始化声卡客户端
	REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC; //采样持续时间，单位100纳秒
	m_pAudioClient->Initialize(
		AUDCLNT_SHAREMODE_SHARED,//与其他设备共享音频端点设备
		0,//选项
		hnsRequestedDuration,//以时间值表示的缓冲区容量
		0,//设备周期，共享模式下设为0
		m_pwfx,//音频格式
		NULL//指向session的GUID的指针，设置为NULL表示打开一个新session
	);

	//获取捕获客户端
	//IAudioCaptureClient *m_pCaptureClient = NULL;
	m_pCaptureClient = NULL;
	hr = m_pAudioClient->GetService(
		IID_IAudioCaptureClient,
		(void**)&m_pCaptureClient);

	//启动音频流
	hr = m_pAudioClient->Start();
}

/*
刷新当前记录器内容
*/
void CRecorder::refreshRecorder()
{
	int count = 10;

	//清空原有数据
	m_recorder.clear();

	UINT32 packetLength = 0;

	//取出缓冲区所有数据
	BYTE *pData = NULL;//数据包首地址
	UINT32 numFramesAvailable;//数据包中可用的音频帧数
	DWORD flags;//缓冲区状态标志
	while (count > 0)
	{
		//获取下一个数据包大小
		
		m_pCaptureClient->GetNextPacketSize(&packetLength);
		while (packetLength != 0)
		{
			//获取缓冲区中的数据
			m_pCaptureClient->GetBuffer(
				&pData,//数据包指针变量的地址
				&numFramesAvailable, //数据包中可用的音频帧数
				&flags, //缓冲区状态标志
				NULL,
				NULL
			);

			//判断是否静音
			if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
			{
				pData = NULL;
			}
			int dataSize = numFramesAvailable * 4;//可用帧数*4=BYTE数

			//采集数据
			for (int i = 0; i < dataSize; i++)
			{
				BYTE tem = pData[i];
				m_recorder.push_back(pData[i]);

			}
			

			//释放缓冲区
			m_pCaptureClient->ReleaseBuffer(numFramesAvailable);

			//获取下一个数据包大小
			m_pCaptureClient->GetNextPacketSize(&packetLength);

		}
		count--;
	}
	BYTE a = 0;
	for (int i = 0; i < m_recorder.size(); i++)
	{
		if (m_recorder[i] != a)
		{
			dataStart = i;
			break;
		}
		if (i == m_recorder.size() - 1)
			dataStart = 1;
	}
}

/*
获取下一份数据
*/
vector<BYTE> CRecorder::captureData(UINT32 nCaptureBytes)
{
	UINT32 packetLength = 0;
	BYTE *pData = NULL;//数据包首地址
	UINT32 numFramesAvailable;//数据包中可用的音频帧数
	DWORD flags;//缓冲区状态标志
	
	vector<BYTE> capturer;//采集器

	while(capturer.size() < nCaptureBytes)
	{
		//获取下一个数据包大小
		m_pCaptureClient->GetNextPacketSize(&packetLength);
	
		while (packetLength != 0)
		{
			if (!(capturer.size() < nCaptureBytes))
			{
				break;//采集够了就退出
			}
			//获取缓冲区中的数据
			m_pCaptureClient->GetBuffer(
				&pData,//数据包指针变量的地址
				&numFramesAvailable, //数据包中可用的音频帧数
				&flags, //缓冲区状态标志
				NULL,
				NULL
			);

			//判断是否静音
			if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
			{
				pData = NULL;
			}
			int dataSize = numFramesAvailable * 4;//可用帧数*4=BYTE数

			//采集数据
			for (int i = 0; i < dataSize; i++)
			{
				if (!(capturer.size() < nCaptureBytes))
				{
					break;//采集够了就退出
				}
				BYTE tem = pData[i];
				capturer.push_back(pData[i]);

			}


			//释放缓冲区
			m_pCaptureClient->ReleaseBuffer(numFramesAvailable);

			//获取下一个数据包大小
			m_pCaptureClient->GetNextPacketSize(&packetLength);

		}

	}
	return capturer;
}
vector<BYTE> CRecorder::captureData()
{
	return captureData(nDefaultCapture);
}
/*
将新采集的数据连接到旧数据m_recorder后面，并保留一定长度的数据
返回新添加的数据长度
*/
int CRecorder::joinData(vector<BYTE> data,int maxSize)
{
	if (data.size() > maxSize) return -1;
	//如果旧数据本身就超过最大长度
	if (m_recorder.size() > maxSize)
	{
		m_recorder.clear();
		for (int i = 0; i < data.size();i++)
		{
			m_recorder.push_back(data[i]);
		}
		return data.size();
	}

	//如果连接后长度超过最大长度，则删除对应长度的旧数据
	if (data.size() + m_recorder.size() > maxSize)
	{
		int del_length = (data.size() + m_recorder.size()) - maxSize;
		if (del_length > maxSize) return -1;//如果大于两倍最大长度直接退出

		vector<BYTE> tmp;
		for (int i = del_length; i < m_recorder.size(); i++)
		{
			tmp.push_back(m_recorder[i]);
		}
		for (int i = 0; i < data.size(); i++)
		{
			tmp.push_back(data[i]);
		}

		m_recorder.clear();
		for (int i = 0; i < tmp.size(); i++)
		{
			m_recorder.push_back(tmp[i]);
		}
		return m_recorder.size();
	}
	
	for (int i = 0; i < data.size(); i++)
	{
		m_recorder.push_back(data[i]);
	}

	return m_recorder.size();
}




/*
错误处理
*/
void CRecorder::onError(HRESULT hres)
{
	if (FAILED(hres)) 
	{
		exit(-1);
	}
}

int CRecorder::getDefaultCaptureNum()
{
	return nDefaultCapture;
}





HRESULT CRecorder::RecordAudioStream()
{
	const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
	const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
	const IID IID_IAudioClient = __uuidof(IAudioClient);
	const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);

	int count = 1;
	HRESULT hr;
	REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
	REFERENCE_TIME hnsActualDuration;
	UINT32 bufferFrameCount;
	UINT32 numFramesAvailable;
	IMMDeviceEnumerator *pEnumerator = NULL;
	IMMDevice *pDevice = NULL;
	IAudioClient *pAudioClient = NULL;
	IAudioCaptureClient *pCaptureClient = NULL;
	WAVEFORMATEX *pwfx = NULL;
	UINT32 packetLength = 0;
	BOOL bDone = FALSE;
	BYTE *pData;
	DWORD flags;

	//创建多媒体枚举器
	hr = CoCreateInstance(
		CLSID_MMDeviceEnumerator, NULL,
		CLSCTX_ALL, IID_IMMDeviceEnumerator,
		(void**)&pEnumerator);
	EXIT_ON_ERROR(hr)

		//获取声卡接口
		hr = pEnumerator->GetDefaultAudioEndpoint(
			eCapture, eConsole, &pDevice);
	EXIT_ON_ERROR(hr)

		//通过声卡接口获取声卡客户端接口
		hr = pDevice->Activate(
			IID_IAudioClient, CLSCTX_ALL,
			NULL, (void**)&pAudioClient);
	EXIT_ON_ERROR(hr)

		//获取音频格式
		hr = pAudioClient->GetMixFormat(&pwfx);
	EXIT_ON_ERROR(hr)
		//初始化声卡客户端
		hr = pAudioClient->Initialize(
			AUDCLNT_SHAREMODE_SHARED,
			0,
			hnsRequestedDuration,
			0,
			pwfx,
			NULL);
	EXIT_ON_ERROR(hr)

		//获取缓冲区大小（有多少音频帧）
		hr = pAudioClient->GetBufferSize(&bufferFrameCount);
	EXIT_ON_ERROR(hr)

		//获取捕获客户端
		hr = pAudioClient->GetService(
			IID_IAudioCaptureClient,
			(void**)&pCaptureClient);
	EXIT_ON_ERROR(hr)

		// Notify the audio sink which format to use.
		//hr = pMySink->SetFormat(pwfx);
		EXIT_ON_ERROR(hr)

		// Calculate the actual duration of the allocated buffer.
		hnsActualDuration = (double)REFTIMES_PER_SEC *
		bufferFrameCount / pwfx->nSamplesPerSec;

	hr = pAudioClient->Start();  // Start recording.
	EXIT_ON_ERROR(hr)

		// Each loop fills about half of the shared buffer.
		while (count > 0)
		{
			printf("正在读取数据包，剩余数据包数%d...\n", count);
			// Sleep for half the buffer duration.
			Sleep(hnsActualDuration / REFTIMES_PER_MILLISEC / 2);

			hr = pCaptureClient->GetNextPacketSize(&packetLength);
			EXIT_ON_ERROR(hr)

				while (packetLength != 0)
				{
					// Get the available data in the shared buffer.
					hr = pCaptureClient->GetBuffer(
						&pData,
						&numFramesAvailable,
						&flags, NULL, NULL);
					EXIT_ON_ERROR(hr)

						if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
						{
							pData = NULL;  // Tell CopyData to write silence.
						}
					int dataSize = numFramesAvailable * 4;//可用帧数*4=BYTE数

					for (int i = 0; i < dataSize; i++)
					{
						BYTE tem = pData[i];
						m_recorder.push_back(pData[i]);

					}



					hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
					EXIT_ON_ERROR(hr)

						hr = pCaptureClient->GetNextPacketSize(&packetLength);
					EXIT_ON_ERROR(hr)
				}
			count--;
		}

	hr = pAudioClient->Stop();  // Stop recording.
	EXIT_ON_ERROR(hr)

		Exit:
	CoTaskMemFree(pwfx);
	SAFE_RELEASE(pEnumerator)
		SAFE_RELEASE(pDevice)
		SAFE_RELEASE(pAudioClient)
		SAFE_RELEASE(pCaptureClient)

		return hr;
}






CRecorder::~CRecorder()
{
	if (m_pAudioClient != NULL) {
		m_pAudioClient->Stop();
	}

	CoUninitialize();
	
}

int CRecorder::coordinateSystem(CDC * pDC, int width, int height,int x_coefficient)
{
	CPen NewPen,NewPen2, *OldPen;//新建坐标系画笔
	NewPen.CreatePen(PS_SOLID, 3, RGB(128, 128, 128));
	OldPen = pDC->SelectObject(&NewPen);
	pDC->MoveTo(0, height);
	pDC->LineTo(width + 5, height);
	pDC->LineTo(width, height - 5);
	pDC->MoveTo(width + 5, height);
	pDC->LineTo(width, height + 5);
	pDC->MoveTo(0, height);
	pDC->LineTo(0, -5);
	pDC->LineTo(-5, 0);
	pDC->MoveTo(0, -5);
	pDC->LineTo(5, 0);
	NewPen.DeleteObject();
	NewPen2.CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
	OldPen = pDC->SelectObject(&NewPen2);
	for (int k = 0; k <= width; k += 10 * x_coefficient)//横坐标系刻度
	{
		if (k == 0)
			continue;
		else if (k % 100 == 0 || (width-k)<10)//大刻度
		{
			pDC->MoveTo(k, height - 8);
			pDC->LineTo(k, height + 8);
		}
		else //小刻度
		{
			pDC->MoveTo(k, height - 4);
			pDC->LineTo(k, height + 4);
		}
		
	}
	for (int i = 0; i <=height ; i += 10 )//纵坐标系刻度
	{
		if ( i == 0 )
			continue;
		else if (i % 50 == 0 || i < 10)//大刻度
		{
			pDC->MoveTo(-8, height-i );
			pDC->LineTo(8, height - i);
		}
		else //小刻度
		{
			pDC->MoveTo(-4, height - i);
			pDC->LineTo(4, height - i);
		}

	}
	NewPen2.DeleteObject();
	return 0;
}

double CRecorder::drawWaveform(CDC* pDC,CRect rect,vector<BYTE> output,double pc)//pc=proportionality coefficient 比例系数
{
	if (output.size() == 0) return 0;
	int height = rect.Height();//一般地，height=273
	int width = rect.Width(); //一般地，width=574
	int x_coefficient = 1;
	int y_transform;
	double maxY = 0;
	//int a = dataStart;
	vector<int> y;
	for (int i = dataStart; i < output.size() && (i-dataStart+1)*x_coefficient <= width; i++)
	{
		if (i % 2 == 0 && i - 1 >= 0)
		{
			//y_transform = bytes2Int(output[i], output[i - 1]);//暂存每个y
			//y_transform = -y_transform + height;
			y.push_back(bytes2Int(output[i], output[i - 1]));
		}
			
	}
	for (int j = 1; j < y.size(); j++)
	{
		if (y[j] > maxY)
			maxY = y[j];
	}
	if (pc == 0)//如果比例系数是0，则使用默认；坐标系非0，则使用前置传入参数
	{
		if (maxY < 64000)//一般地，maxY = 65535;如果小于该值则确定该值为65535，保持比例稳定
			maxY = 65535;
		pc= maxY * 1.5;
	}
	//绘制坐标系
	coordinateSystem(pDC, width, height,x_coefficient);
	CPen NewPen, *OldPen;
	NewPen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
	OldPen = pDC->SelectObject(&NewPen);
	pDC->MoveTo(0, output[dataStart]);
	for (int k = 1; k < y.size(); k++)
	{
		y_transform = coordinateTrans(y[k],pc,height);
		//y_transform = - y_transform + height;
		pDC->LineTo(k*2*x_coefficient, y_transform);
	}
	CString str1, str2;
	str1.Format(_T("%lf"), maxY);
	str2.Format(_T("%lf"), pc);
	//str3.Format(_T("%d"), width);
	pDC->TextOutW(width/2-80, -20  , _T("最大值") + str1);
	pDC->TextOutW(width/2-80, 0 , _T("比例系数") + str2);
	//pDC->TextOutW(width, 0, str3);
	//y = coordinateTrans(y, height);
	//pDC->LineTo((i - dataStart + 1) * x_coefficient, y);
	return pc;
}

/*
坐标转换函数
将纵坐标数据从数据坐标转换到设备坐标
*/
double CRecorder::coordinateTrans(int y,double maxY,int height) 
{
	return -(y /  maxY ) * height + height;
}

/*
绘制频域图像
*/
int CRecorder::drawFrequency(CDC * pDC, CRect rect, vector<BYTE> output)
{
	int height = rect.Height();
	int width = rect.Width();

	
	if (output.size() == 0)
	{
		return -1;
	}
	double delta_f = m_pwfx->nSamplesPerSec / (output.size()/2);//频谱分辨率
		
	int x_coefficient = 5;
	for (int i = output.size(); i <= nDefaultCapture; i++)
	{
		output.push_back(0);//若长度不足，则补零
	}
	//使用时域信号初始化系数数组
	vector<complex<double>> A;//事实上长度只有output数组的一半
	for (int i = 1; i <= nDefaultCapture; i++)
	{
		if (i % 2 == 0)
		{//两个Byte为一个有效数据
			int data = bytes2Int(output[i], output[i - 1]);
			complex<double> ai(data, 0);
			A.push_back(ai);
		}	
	}

	A = FFT(A);//快速傅里叶变换


	//找出最大值以确定显示比例
	vector<double> y_list;//存储y值
	double yMax = Model(A[1]);
	double main_frequency = delta_f;//主频
	for (int i = 1; i < A.size(); i++)//直流分量不计算在内
	{
		double y = Model(A[i]);
		if (y > yMax)
		{
			yMax = y;
			main_frequency = delta_f * i;
		}
		y_list.push_back(y);
	}

	//绘制坐标系
	coordinateSystem(pDC, width, height, x_coefficient);

	int y_trans = Round(coordinateTrans(y_list[0],yMax, height));
	pDC->MoveTo(0, y_trans);
	for (int i = 1; i < y_list.size(),i*x_coefficient <= width; i++)
	{
		y_trans = Round(coordinateTrans(y_list[i], yMax, height));
		pDC->LineTo(i * x_coefficient, y_trans);
	}

	CString data;
	data.Format(_T("主频：%d Hz"), Round(main_frequency));
	pDC->TextOut(width / 2, height / 2, data);
	

	return 0;
}

int CRecorder::numericalAnalysis(CDC * pDC, CRect rect)
{
	CString str;
	long long sum=0;
	vector<long long> ana;//ana for analysis
	for (int i = 1; i <= nDefaultCapture; i++)
	{
		ana.push_back((i - 1)*(m_pwfx->nSamplesPerSec)* nDefaultCapture /1000 );//由于数值过大，设置单位为千赫兹
	}
	for (int j = 0; j < ana.size(); j++)
		sum += ana[j];
	sum /= ana.size();
	str.Format(_T("%u"), sum );//不太清楚usigned int 64 转 CString是否是这样的，目前数值不存在丢失
	pDC->TextOutW(0,0,_T("主频：")+str+_T("kHZ"));
	return 0;
}



vector<BYTE> CRecorder::NoFiltering()
{
	return m_recorder;
}
vector<BYTE> CRecorder::LimiterFiltering(vector<BYTE> input)
{
	//限幅滤波法
	int A = 100;
	vector<BYTE> temp(input);
	for (int i = dataStart+1; i < input.size(); i++)
	{
		if ((input[i] - input[i - 1]) > A || (input[i - 1] - input[i]) > A)
			temp[i] = input[i - 1];
	}
	return temp;
}

vector<BYTE> CRecorder::Mean_Filtering(vector<BYTE> input)
{
	//均值滤波法
	int sum = 0; int scale = 5;
	vector<BYTE>temp;
	for (int i = 0; i < input.size() - scale; ++i) {
		for (int j = 0; j < scale; ++j) {
			sum += input.at(i + j);
		}
		temp.push_back((int)(sum / scale));
		sum = 0;
	}
	return temp;

}
vector<BYTE> CRecorder::Median_Filtering(vector<BYTE> input)
{
	//中值滤波法
	int scale = 5;
	vector<BYTE>dst, temp;
	dst.resize(scale);
	for (int i = 0; i < input.size() - scale; ++i) {
		partial_sort_copy(input.begin() + i, input.begin() + i + scale, dst.begin(), dst.end());
		temp.push_back(dst.at((int)(scale / 2)));
	}
	return temp;
}
/*
*FFT
传入的复数数组里面都是实数，含义是多项式系数表示法的系数，值为时域幅值
系数数组长度得是2的整数次方
返回值的模为频谱幅值
*/
vector<complex<double>> CRecorder::FFT(vector<complex<double>> A)
{
	const double PI = 3.141592651;
	int length = A.size();
	if (length == 1) return A;//递归结束条件
	vector<complex<double>> A1, A2;//A(x) = A1(x^2) + x * A2(x^2)
	//将系数分类
	for (int i = 0; i < length; i++)
	{
		if (i % 2 == 0)
			A1.push_back(A[i]);
		else
			A2.push_back(A[i]);
	}

	A1 = FFT(A1);
	A2 = FFT(A2);

	complex<double> Wn(cos(2.0*PI / length), sin(2.0*PI / length));//len等分点的角度增量
	complex<double> W(1.0, 0.0);//用于遍历复平面单位圆上的len个等分点

	for (int i = 0; i * 2 < length; i++, W *= Wn)
	{
		A[i] = A1[i] + W * A2[i];
		A[i + length / 2] = A1[i] - W * A2[i];
	}
	return A;
}

int CRecorder::bytes2Int(BYTE higher, BYTE lower)
{	
	int res = 0;
	if (Islittlend())
	{//小端序
		res = lower | higher << 8;
	}
	else
	{//大端序
		res = higher | lower << 8;
	}
	return res;
}

int CRecorder::Islittlend()
{
	int a = 0x1;
	char* b = (char*)&a; //将a的地址强转成char*类型并存于b中
	if (*b == 0x0) {
		return 0;
		//大端字节序
	}
	return 1;//小端字节序
}
